package com.wzcl.app.service.impl;

import com.wzcl.app.dao.model.*;
import com.wzcl.app.dao.repository.OrdersRepository;
import com.wzcl.app.dao.repository.RefundRequestRepository;
import com.wzcl.app.model.common.*;
import com.wzcl.app.model.dto.RefundRequestDto;
import com.wzcl.app.model.vo.RefundRequestVo;
import com.wzcl.app.service.OrderService;
import com.wzcl.app.service.RefundRequestService;
import com.wzcl.app.service.UserTopUpService;
import com.wzcl.app.service.common.MyPayService;
import com.wzcl.app.service.common.PayServiceFactory;
import com.wzcl.app.utils.CollectionUtil;
import com.wzcl.app.utils.LockUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

@Log4j2
@Service
@Transactional
public class RefundRequestServiceImpl implements RefundRequestService {
    private static final String CREATE_LOCK = "REFUND_CREATE_LOCK_";
    @Resource
    private RefundRequestRepository repository;
    @Resource
    private OrderService orderService;
    @Resource
    private UserTopUpService userTopUpService;
    @Resource
    private OrdersRepository ordersRepository;

    @Override
    public RVO addRefundRequest(RefundRequestDto refundRequestDto) {
        //查询订单中生成订单的系统
        Integer sysType = ordersRepository.findAllByOrderNumber(refundRequestDto.getOrderNo()).getSysType();
        //申请退款
        if (sysType == 0){
            //苹果退款
            return userTopUpService.applyReturnLearnCoin(refundRequestDto.getOrderNo(), refundRequestDto.getReason());
        }
        //非苹果
        if (LockUtil.isLocked(CREATE_LOCK + refundRequestDto.getOrderNo())) {
            throw new BusinessException(CodeMsg.REFUND_REQUEST_CREATING);
        }
        LockUtil.lock(CREATE_LOCK + refundRequestDto.getOrderNo(), 5 * 60);
        try {
            //校验获取订单
            Orders order = orderService.getAndCheckByNo(refundRequestDto.getOrderNo());
            //校验用户
            String phone = SecurityContextHolder.getContext().getAuthentication().getName();
            if (!phone.equals(order.getUserPhone())) {
                throw new BusinessException(CodeMsg.USER_NOT_MATCH);
            }
            //尚未支付
            if (!WZCL.PAY_STATE.PAID.equals(order.getPayState())) {
                throw new BusinessException(CodeMsg.ORDER_OPERATE_FAIL, "订单尚未支付");
            }

            //TODO 支付记录哈哈哈
            /*PayRecord record = payRecordService.getAndCheckByOrderNo(order.getNo());
            //支持退款校验
            if (!record.isSupportRefund()) {
                throw new BusinessException(CodeMsg.ORDER_OPERATE_FAIL, "订单不支持退款功能");
            }*/
            orderService.setPayStateByOrderNo(WZCL.PAY_STATE.BE_REFUND, order.getNo());
            RefundRequest refundRequest = new RefundRequest();
            refundRequest.setReason(refundRequestDto.getReason());
            refundRequest.setOrderNo(order.getNo());
            refundRequest.setAuditState(WZCL.AUDIT_STATE.PENDING);
            return RVO.success(RefundRequestVo.create(repository.save(refundRequest)));
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("退款申请失败:{} \r\n 堆栈信息:{}", e.getMessage(), BusinessException.collectExceptionStackMsg(e));
            throw new BusinessException(CodeMsg.SYSTEM_ERR);
        } finally {
            LockUtil.unlock(CREATE_LOCK + refundRequestDto.getOrderNo());
        }
    }

    @Override
    public RVO auditRefundRequest(RefundRequestDto refundRequestDto) {
        //查询订单中生成订单的系统
        Integer sysType = ordersRepository.findAllByOrderNumber(refundRequestDto.getOrderNo()).getSysType();
        //判断订单是学币退款订单（苹果）还是实际支付订单（安卓）
        if (sysType == 0){
            //苹果退款
            return userTopUpService.returnLearnCoin(refundRequestDto.getOrderNo(), refundRequestDto.getAuditState(), refundRequestDto.getMessage());
        }

        //校验获取
        RefundRequest request = getAndCheckById(refundRequestDto);

        //操作校验
        if (!WZCL.AUDIT_STATE.PENDING.equals(request.getAuditState())) {
            throw new BusinessException(CodeMsg.REFUND_REQUEST_OPERATE_FAIL, "非待审核申请无法操作");
        }
        //获取订单
        Orders order = orderService.getAndCheckByNo(request.getOrderNo());
        if (WZCL.AUDIT_STATE.PASSED.equals(refundRequestDto.getAuditState())) {
            //TODO 支付记录哈哈哈
            /*//审核通过
            PayRecord payRecord = payRecordService.getAndCheckByOrderNo(request.getOrderNo());
            //退款(通过回调的异步通知改变状态)
            MyPayService myPayService = PayServiceFactory.makePayService(payRecord.getPayType());
            myPayService.refund(payRecord);*/
        } else if (WZCL.AUDIT_STATE.REJECTED.equals(refundRequestDto.getAuditState())) {
            //驳回请求
            if (StringUtils.isBlank(refundRequestDto.getMessage())) {
                throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "驳回请求需要填写驳回原因");
            }
            request.setMessage(refundRequestDto.getMessage());
            //驳回:订单状态改变
            orderService.setPayStateByOrderNo(WZCL.PAY_STATE.PAID, order.getNo());
        } else {
            throw new BusinessException(CodeMsg.AUDIT_STATE_ERROR);
        }
        request.setAuditState(refundRequestDto.getAuditState());

        return RVO.success(RefundRequestVo.create(repository.save(request)));
    }

    @Override
    public RefundRequest getRefundRequestById(Integer id) {
        return repository.findById(id).orElse(null);
    }

    @Override
    public RefundRequest getAndCheckById(Integer id) {
        if (id == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少退款申请ID");
        }
        RefundRequest request = getRefundRequestById(id);
        if (request == null) {
            throw new BusinessException(CodeMsg.REFUND_REQUEST_NOT_EXIST);
        }
        return request;
    }

    @Override
    public List<RefundRequest> getRefundRequestList(RefundRequestDto refundRequestDto) {
        return repository.findAll(getSpecification(refundRequestDto), refundRequestDto.getSort());
    }

    @Override
    public MyPage<RefundRequest> getRefundRequestPage(RefundRequestDto refundRequestDto) {
        String phone = SecurityContextHolder.getContext().getAuthentication().getName();
        List<RefundRequest> all = repository.findAll(getSpecification(refundRequestDto));
        ArrayList<RefundRequest> list = new ArrayList<>(all);
        //将所有记录排序
        List<RefundRequest> listBySort = CollectionUtil.getListBySort(list, "getCreateTime", "desc");
        for (int index = 0; index < listBySort.size(); index++) {
            //获取当前用户订单创建时间
            Date createTime = ordersRepository.findAllByOrderNumber(listBySort.get(index).getOrderNo()).getCreateTime();
            listBySort.get(index).setOrderCreateTime(createTime);
            listBySort.get(index).setUserPhone(phone);
        }
        List<RefundRequest> listByPage = CollectionUtil.getListByPage(listBySort, refundRequestDto.getPageNumber(), refundRequestDto.getPageSize());
        MyPage<RefundRequest> myPage = new MyPage<>();
        myPage.setContent(listByPage);
        myPage.setTotalElements(list.size());
        return myPage;
    }

    private Specification<RefundRequest> getSpecification(RefundRequestDto refundRequestDto) {
        return new Specification<RefundRequest>() {
            @Override
            public Predicate toPredicate(Root<RefundRequest> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (refundRequestDto.getAuditState() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("auditState"), refundRequestDto.getAuditState()));
                }
                if (StringUtils.isNotBlank(refundRequestDto.getOrderNo())) {
                    predicates.add(criteriaBuilder.equal(root.get("orderNo"), refundRequestDto.getOrderNo()));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }
}
